﻿using System;
using System.Collections.Generic;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Xml;
using System.Xml.Linq;
using Components;
using WorkflowComponentBase=Components.WorkflowComponentBase;
using WorkflowDesign=Components.WorkflowDesign;

namespace WorkflowDesigner
{
    public partial class WorkflowDocument : UserControl
    {
        #region events
        public event OnNodeSelected NodeSelected;
        public event OnWorkflowDesignSelected WorkflowDesignSelected;
        public event OnReleaseToolItem ReleaseToolItem;
        #endregion

        #region fields
        private WorkflowDesign _Design;
        private readonly Content content = Application.Current.Host.Content;
        private int MinorGridSize = 3;
        private int MajorGridSize = 30;
        private int GridStart = 0;
        private readonly Color MinorGridColor = Color.FromArgb(50, 245, 245, 245);
        private readonly Color MajorGridColor = Color.FromArgb(100, 230, 230, 230);
        #endregion

        #region props
        public WorkflowDesign Design
        {
            get
            {
                this.UpdateWorkflowDesign();
                return this._Design;
            }
        }

        public WorkflowComponentBase DraggedObject { get; set; }
        #endregion

        #region init
        public WorkflowDocument()
        {
            InitializeComponent();
            this._Design=new WorkflowDesign();
            this.canvasDiagram.MouseEnter += new MouseEventHandler(canvasDiagram_MouseEnter);
            this.canvasDiagram.MouseLeftButtonUp += new MouseButtonEventHandler(canvasDiagram_MouseLeftButtonUp);
            this.KeyDown += new KeyEventHandler(WorkflowDocument_KeyDown);
            this.MouseLeftButtonDown += new MouseButtonEventHandler(WorkflowDocument_MouseLeftButtonDown);
            content.FullScreenChanged += new EventHandler(content_FullScreenChanged);
            this.SizeChanged += new SizeChangedEventHandler(WorkflowDocument_SizeChanged);
        }

        private void WorkflowDocument_MouseLeftButtonDown(object msender, MouseButtonEventArgs me)
        {
            this.HandleBackgroundClick();
        }

        void line_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.HandleBackgroundClick();
        }

        void docRect_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.HandleBackgroundClick();
        }

        private void HandleBackgroundClick()
        {
            if (this.WorkflowDesignSelected != null)
                this.WorkflowDesignSelected(this._Design);
            foreach (FrameworkElement fe in this.canvasDiagram.Children)
            {
                if (fe is Node)
                    ((Node)fe).Selected = false;
            }
        }

        void canvasDiagram_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            foreach(FrameworkElement fe in this.canvasDiagram.Children)
            {
                if(fe is Node)
                {
                    ((Node)fe).TrackingMouseMovement = false;
                }
            }
        }

        void canvasDiagram_MouseEnter(object sender, MouseEventArgs e)
        {
            if(this.DraggedObject !=null)
            {
                Node node = new Node(this.DraggedObject.Clone());
                node.Initiated = true;
                node.TrackingMouseMovement = true;
                node.NodeSelected+=new OnNodeSelected(node_NodeSelected);
                this.canvasDiagram.Children.Add(node);
                this.DraggedObject = null;
                if (this.ReleaseToolItem != null)
                    this.ReleaseToolItem();
            }
        }

        void WorkflowDocument_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            this.DrawGraphPaper();
        }

        void WorkflowDocument_Loaded(object sender, RoutedEventArgs e)
        {
            this.DrawGraphPaper();
            //this.PopulateAvailableActivities();
        }

        private void DrawGraphPaper()
        {
            //this._Design.FileName = this.Save();
            //this.OpenLocalWorkflow(this._Design.FileName);
            Rectangle docRect=new Rectangle();
            docRect.Fill = new SolidColorBrush(Colors.White);
            docRect.Width = this.canvasDiagram.ActualWidth;
            docRect.Height = this.canvasDiagram.ActualHeight;
            this.canvasDiagram.Children.Add(docRect);
            docRect.SetValue(Canvas.ZIndexProperty,0);
            docRect.MouseLeftButtonDown += new MouseButtonEventHandler(docRect_MouseLeftButtonDown);
            Line line;
            for (int i = MinorGridSize*(GridStart + 1); i < this.canvasDiagram.ActualWidth; i += MinorGridSize)
            {
                line = new Line();
                if (i%MajorGridSize == 0)
                    line.Stroke = new SolidColorBrush(this.MajorGridColor);
                else
                    line.Stroke = new SolidColorBrush(this.MinorGridColor);
                line.StrokeThickness = 1;
                line.X1 = i;
                line.Y1 = 0;
                line.X2 = i;
                line.Y2 = this.canvasDiagram.ActualHeight;
                this.canvasDiagram.Children.Add(line);
                line.MouseLeftButtonDown += new MouseButtonEventHandler(line_MouseLeftButtonDown);
                line.SetValue(Canvas.ZIndexProperty,0);
            }
            for (int i = MinorGridSize; i < this.canvasDiagram.ActualHeight; i += MinorGridSize)
            {
                line = new Line();
                if (i%MajorGridSize == 0)
                    line.Stroke = new SolidColorBrush(this.MajorGridColor);
                else
                    line.Stroke = new SolidColorBrush(this.MinorGridColor);
                line.StrokeThickness = 1;
                line.X1 = MinorGridSize*GridStart;
                line.Y1 = i;
                line.X2 = this.canvasDiagram.ActualWidth;
                line.Y2 = i;
                this.canvasDiagram.Children.Add(line);
                line.MouseLeftButtonDown+=new MouseButtonEventHandler(line_MouseLeftButtonDown);
                line.SetValue(Canvas.ZIndexProperty, 0);
            }
        }


        //private void PopulateAvailableActivities()
        //{
        //    double x = 3, y = 3;
        //    Timer timer = new Timer();
        //    ToolItem toolItem = new ToolItem(timer);
        //    toolItem.DragToolItem += new OnDragToolItem(toolItem_OnDragToolItem);
        //    this.panelToolItems.Children.Add(toolItem);
        //    toolItem.SetValue(Canvas.LeftProperty, x);
        //    toolItem.SetValue(Canvas.TopProperty, y);
        //    y += toolItem.IconSize.Height + 3;
        //    EmailComponent email=new EmailComponent();
        //    toolItem = new ToolItem(email);
        //    toolItem.DragToolItem += new OnDragToolItem(toolItem_OnDragToolItem);
        //    this.panelToolItems.Children.Add(toolItem);
        //    toolItem.SetValue(Canvas.LeftProperty, x);
        //    toolItem.SetValue(Canvas.TopProperty, y);
            
        //}

        //void toolItem_OnDragToolItem(WorkflowComponentBase userObj)
        //{
        //    this.DraggedObject = userObj;
        //}
        #endregion

        #region mouse/keyboard event handlers
        void content_FullScreenChanged(object sender, EventArgs e)
        {
            if(content.IsFullScreen)
            {
                double heightRatio = content.ActualHeight/this.Height;
                double widthRadio = content.ActualWidth/this.Width;
                ScaleTransform scaleTransform=new ScaleTransform();
                scaleTransform.ScaleX = widthRadio;
                scaleTransform.ScaleY = heightRatio;
                this.RenderTransform = scaleTransform;
            }
            else
            {
                this.RenderTransform = null;
            }
        }

        void WorkflowDocument_KeyDown(object sender, KeyEventArgs e)
        {
            if(e.Key==Key.F)
            {
                content.IsFullScreen = true;
            }
            else if(e.Key == Key.Delete)
            {
                for(int i=0;i<this.canvasDiagram.Children.Count;i++)
                {
                    if(this.canvasDiagram.Children[i] is Node)
                    {
                        Node node = this.canvasDiagram.Children[i] as Node;
                        if(node.Selected)
                        {
                            node.Delete();
                        }
                    }
                }
            }
        }
        #endregion

        #region copy/paste/cut

        #endregion

        #region undo/redo

        #endregion

        #region save/open
        public string Save()
        {
            string fileName = this.GetWorkflowFileName();
            this.SaveAs(fileName);
            return fileName;
        }

        public void Publish()
        {
            this.Save();
            this._Design.Publish();
        }

        private string GetWorkflowFileName()
        {
            if (string.IsNullOrEmpty(this._Design.WorkflowName))
            {
                this._Design.WorkflowName = "New workflow";
            }
            string fileName = this._Design.WorkflowName + ".wox";
            return fileName;
        }

        public void SaveAs(string workflowFileName)
        {
            this.UpdateWorkflowDesign();
            this._Design.WorkflowName = workflowFileName;
            if (this._Design.WorkflowName.IndexOf(".wox") > 0)
                this._Design.WorkflowName = this._Design.WorkflowName.Substring(
                    0, this._Design.WorkflowName.IndexOf(".wox"));
            this._Design.FileName = workflowFileName;
            WorkflowFALC.SaveWorkflow(this._Design);
        }

        private void UpdateWorkflowDesign()
        {
            this._Design.ModifiedOn = DateTime.Now;
            Dictionary<Guid,WorkflowComponentBase> components=new Dictionary<Guid, WorkflowComponentBase>();
            Dictionary<Guid, List<Guid>> successConnectors = new Dictionary<Guid, List<Guid>>();
            Dictionary<Guid, List<Guid>> failureConnectors = new Dictionary<Guid, List<Guid>>();
            foreach (FrameworkElement fe in this.canvasDiagram.Children)
            {
                if (fe is Node)
                {
                    Node node = (Node)fe;
                    WorkflowComponentBase wfComponent = node.UserObject;
                    wfComponent.ComponentName = node.Label.Text;
                    wfComponent.X = (double)node.Image.GetValue(Canvas.LeftProperty);
                    wfComponent.Y = (double)node.Image.GetValue(Canvas.TopProperty);
                    components.Add(wfComponent.ComponentID, wfComponent);
                }
            }
            foreach (FrameworkElement fe in this.canvasDiagram.Children)
            {
                if (fe is Connector)
                {
                    Connector connector = (Connector)fe;
                    Guid fromID = connector.FromPort.OwnerNode.UserObject.ComponentID;
                    Guid toID = connector.ToPort.OwnerNode.UserObject.ComponentID;
                    if(connector.FromPort.PortType==PortType.SuccessOutput)
                    {
                        if(successConnectors.ContainsKey(fromID))
                        {
                            List<Guid> toIDs = successConnectors[fromID];
                            toIDs.Add(toID);
                            successConnectors[fromID] = toIDs;
                        }
                        else
                        {
                            List<Guid> toIDs = new List<Guid>();
                            toIDs.Add(toID);
                            successConnectors.Add(fromID, toIDs);
                        }
                    }
                    else
                    {
                        if (failureConnectors.ContainsKey(fromID))
                        {
                            List<Guid> toIDs = failureConnectors[fromID];
                            toIDs.Add(toID);
                            failureConnectors[fromID] = toIDs;
                        }
                        else
                        {
                            List<Guid> toIDs = new List<Guid>();
                            toIDs.Add(toID);
                            failureConnectors.Add(fromID, toIDs);
                        }
                    }
                }
            }
            
            // dependencies
            foreach(WorkflowComponentBase component in this._Design.Components.Values)
            {
                component.Dependencies=new List<Guid>();
            }
            foreach(Guid fromID in this._Design.SuccessConnectors.Keys)
            {
                List<Guid> toIDs = this._Design.SuccessConnectors[fromID];
                foreach(Guid toID in toIDs)
                {
                    WorkflowComponentBase component = this._Design.Components[toID];
                    if (component.Dependencies == null)
                        component.Dependencies = new List<Guid>();
                    if(!component.Dependencies.Contains(fromID))
                        component.Dependencies.Add(fromID);
                }
            }
            foreach (Guid fromID in this._Design.FailureConnectors.Keys)
            {
                List<Guid> toIDs = this._Design.FailureConnectors[fromID];
                foreach (Guid toID in toIDs)
                {
                    WorkflowComponentBase component = this._Design.Components[toID];
                    if (component.Dependencies == null)
                        component.Dependencies = new List<Guid>();
                    if (!component.Dependencies.Contains(fromID))
                        component.Dependencies.Add(fromID);
                }
            }
            this._Design.Components = components;
            this._Design.SuccessConnectors = successConnectors;
            this._Design.FailureConnectors = failureConnectors;
        }

        public void OpenLocalWorkflow(string workflowFileName)
        {
            string workflowName = workflowFileName;
            if (workflowName.IndexOf(".wox") > 0)
                workflowName = workflowName.Substring(0, workflowName.IndexOf(".wox"));
            XDocument xDoc = WorkflowFALC.ReadWorkflow(workflowName);
            this._Design = WorkflowDesign.Read(xDoc);

            this.DisplayWorkflow();
        }

        public void OpenServerWorkflow(string workflowName)
        {
            WorkflowDesign wfDesign=new WorkflowDesign();
            wfDesign.PublishedWorkflowOpened += new WorkflowDesign.OnPublishedWorkflowOpened(wfDesign_PublishedWorkflowOpened);
            wfDesign.GetPublishedWorkflow(workflowName);
        }

        void wfDesign_PublishedWorkflowOpened(WorkflowDesign wfDesign)
        {
            this._Design = wfDesign;
            this.DisplayWorkflow();
        }

        private void DisplayWorkflow()
        {
            this.canvasDiagram.Children.Clear();
            this.DrawGraphPaper();

            Dictionary<Guid, Node> nodes = new Dictionary<Guid, Node>();
            foreach (WorkflowComponentBase component in this._Design.Components.Values)
            {
                Node node = new Node(component);
                node.NodeSelected += new OnNodeSelected(node_NodeSelected);
                this.canvasDiagram.Children.Add(node);
                node.MoveTo(new Point(component.X, component.Y));
                node.SetValue(Canvas.ZIndexProperty, 2);
                nodes.Add(component.ComponentID, node);
            }
            foreach (Guid fromID in this._Design.SuccessConnectors.Keys)
            {
                if(!nodes.ContainsKey(fromID))
                    continue;
                List<Guid> toIDs = this._Design.SuccessConnectors[fromID];
                foreach (Guid toID in toIDs)
                {
                    if(!nodes.ContainsKey(toID))
                        continue;
                    PortType portType = PortType.SuccessOutput;
                    Node fromNode = nodes[fromID];
                    Node toNode = nodes[toID];
                    fromNode.MakeConnection(portType, toNode);
                }
            }
            foreach (Guid fromID in this._Design.FailureConnectors.Keys)
            {
                if (!nodes.ContainsKey(fromID))
                    continue;
                List<Guid> toIDs = this._Design.FailureConnectors[fromID];
                foreach (Guid toID in toIDs)
                {
                    if (!nodes.ContainsKey(toID))
                        continue;
                    PortType portType = PortType.ErrorOutput;
                    Node fromNode = nodes[fromID];
                    Node toNode = nodes[toID];
                    fromNode.MakeConnection(portType, toNode);
                }
            }
        }

        void node_NodeSelected(Node node)
        {
            if (this.NodeSelected != null)
                this.NodeSelected(node);
        }
        #endregion

        #region test
        public void TestWorkflow()
        {
            this.Save();
            this._Design.RunWorkflow();
        }
        #endregion

        #region print
        public void PrintWorkflow()
        {
            
        }
        #endregion
    }
}
